#include "LeGraphicsLayerStackModel.h"

#include "LeGraphicsItem.h"
#include "LeGraphicsItemLayer.h"
#include "LeGraphicsScene.h"

#include <core/core.h>

#include <QDebug>

class GraphicsLayerStackItem
{
    GraphicsLayerStackItem(LeGraphicsItemLayer *aLayer)
        : layer(aLayer)
    {}

    LeGraphicsItemLayer *layer;
};

class LeGraphicsLayerStackModelPrivate
{
    Q_DISABLE_COPY(LeGraphicsLayerStackModelPrivate)
    Q_DECLARE_PUBLIC(LeGraphicsLayerStackModel)
    LeGraphicsLayerStackModel * const q_ptr;

    explicit LeGraphicsLayerStackModelPrivate(LeGraphicsLayerStackModel *model):
        q_ptr(model)
    {}

    LeGraphicsScene *m_scene = nullptr;
};

LeGraphicsLayerStackModel::LeGraphicsLayerStackModel(QObject *parent):
    QAbstractTableModel(parent),
    d_ptr(new LeGraphicsLayerStackModelPrivate(this))
{
}

LeGraphicsLayerStackModel::~LeGraphicsLayerStackModel()
{

}

void LeGraphicsLayerStackModel::setScene(LeGraphicsScene *scene)
{
    Q_D(LeGraphicsLayerStackModel);

    if (d->m_scene != nullptr)
        d->m_scene->disconnect(this);

    beginResetModel();
    d->m_scene = scene;
    endResetModel();

    if (d->m_scene != nullptr)
    {
        connect(d->m_scene, &LeGraphicsScene::aboutToAddLayer,
                this, &LeGraphicsLayerStackModel::beginResetModel);
        connect(d->m_scene, &LeGraphicsScene::layerAdded,
                this, &LeGraphicsLayerStackModel::endResetModel);
        connect(d->m_scene, &LeGraphicsScene::aboutToRemoveLayer,
                this, &LeGraphicsLayerStackModel::beginResetModel);
        connect(d->m_scene, &LeGraphicsScene::layerRemoved,
                this, &LeGraphicsLayerStackModel::endResetModel);
        connect(d->m_scene, &LeGraphicsScene::aboutToChangeLayerOrdering,
                this, &LeGraphicsLayerStackModel::beginResetModel);
        connect(d->m_scene, &LeGraphicsScene::layerOrderingChanged,
                this, &LeGraphicsLayerStackModel::endResetModel);
    }
}

LeGraphicsItemLayer *LeGraphicsLayerStackModel::graphicsLayer(const QString &name) const
{
    Q_D(const LeGraphicsLayerStackModel);

    if (d->m_scene == nullptr)
        return nullptr;

    for (LeGraphicsItemLayer *layer: d->m_scene->featureLayers())
        if (layer->fullName() == name)
            return layer;

    return nullptr;
}

LeGraphicsItemLayer *LeGraphicsLayerStackModel::graphicsLayer(const QModelIndex &index) const
{
    Q_D(const LeGraphicsLayerStackModel);

    if (!index.isValid())
        return nullptr;

    if (d->m_scene == nullptr)
        return nullptr;

    return d->m_scene->featureLayers().value(index.row(), nullptr);
}

QModelIndex LeGraphicsLayerStackModel::moveToTop(const QModelIndex &index)
{
    if (moveRow(QModelIndex(), index.row(), QModelIndex(), 0))
        return createIndex(0, 0);
    return index;
}

QModelIndex LeGraphicsLayerStackModel::moveToBottom(const QModelIndex &index)
{
    if (moveRow(QModelIndex(), index.row(), QModelIndex(), rowCount()))
        return createIndex(rowCount() - 1, 0);
    return index;
}

QModelIndex LeGraphicsLayerStackModel::moveUp(const QModelIndex &index)
{
    if (moveRow(QModelIndex(), index.row(), QModelIndex(), index.row() - 1))
        return createIndex(index.row() - 1, 0);
    return index;
}

QModelIndex LeGraphicsLayerStackModel::moveDown(const QModelIndex &index)
{
    if (moveRow(QModelIndex(), index.row(), QModelIndex(), index.row() + 2))
        return createIndex(index.row() + 1, 0);
    return index;
}

QVariant LeGraphicsLayerStackModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation != Qt::Horizontal)
        return QVariant();

    if (role != Qt::DisplayRole)
        return QVariant();

    switch (section)
    {
        case VisibleColumn: return "Vis.";
        case SequenceNumberColumn: return "Seq";
        case NameColumn: return "Name";
        default: return "?";
    }
}


int LeGraphicsLayerStackModel::rowCount(const QModelIndex &parent) const
{
    Q_D(const LeGraphicsLayerStackModel);
    Q_UNUSED(parent);

    if (d->m_scene != nullptr)
        return d->m_scene->featureLayers().count();
    return 0;
}

int LeGraphicsLayerStackModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return ColumnCount;
}

QVariant LeGraphicsLayerStackModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    Q_D(const LeGraphicsLayerStackModel);

    const LeGraphicsItemLayer *layer = d->m_scene->featureLayers().at(index.row());
    switch (role)
    {
        case Qt::DisplayRole:
            switch (index.column())
            {
                case SequenceNumberColumn:
                    return 0;
                case NameColumn:
                    return layer->shortName();
                case CompactColumn:
                    return layer->tinyName();
                default:
                    return QVariant();
            }
            break;
        case Qt::DecorationRole:
            switch (index.column())
            {
                case VisibleColumn:
                    return layer->isVisible() ? Core::icon("layer-visible-on") :
                                                Core::icon("layer-visible-off");
                    break;
                default:
                    break;
            }
            break;
        case Qt::ToolTipRole:
            return layer->fullName();
        case Qt::BackgroundRole:
            return layer->featureOption().featureBrush(GftPad);
        case Qt::CheckStateRole:
            switch (index.column())
            {
                case VisibleColumn:
                case CompactColumn:
                    return QVariant::fromValue<Qt::CheckState>(layer->isVisible() ?
                                                                   Qt::Checked: Qt::Unchecked);
                default:
                    break;
            }
            break;
        default:
            break;
    }
    return QVariant();
}

bool LeGraphicsLayerStackModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    Q_D(LeGraphicsLayerStackModel);

    if (data(index, role) == value)
        return false;

    LeGraphicsItemLayer *layer = d->m_scene->featureLayers().at(index.row());
    switch (role)
    {
        case Qt::CheckStateRole:
            switch (index.column())
            {
                case VisibleColumn:
                case CompactColumn:
                {
                    Qt::CheckState state = value.value<Qt::CheckState>();
                    if (state == Qt::Checked)
                        layer->setVisible(true);
                    else
                        layer->setVisible(false);
                    emit dataChanged(index, index);
                    return true;
                }
                default:
                    break;
            }
        default:
            break;
    }

    return false;
}

Qt::ItemFlags LeGraphicsLayerStackModel::flags(const QModelIndex &index) const
{
    //Q_D(const GraphicsLayerStackModel);

    if (!index.isValid())
        return Qt::NoItemFlags;

    Qt::ItemFlags itemFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;

    switch (index.column())
    {
        case VisibleColumn:
        case CompactColumn:
            itemFlags.setFlag(Qt::ItemIsUserCheckable);
            break;
        default:
            break;
    }

    return itemFlags;
}

bool LeGraphicsLayerStackModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count,
                                     const QModelIndex &destinationParent, int destinationChild)
{
    Q_D(LeGraphicsLayerStackModel);

    // Table model, parent is always invalid
    QModelIndex parent;
    if (sourceParent.isValid() || destinationParent.isValid())
        return false;

    // More than one row is not supported (yet)
    if (count > 1)
        return false;

    if (sourceRow < 0 || sourceRow >= rowCount())
        return false;

    if (destinationChild < 0 || destinationChild > rowCount())
        return false;

    // Cannot move X above X
    if (destinationChild == sourceRow)
        return false;

    // Cannot move X above X+1
    if (destinationChild == sourceRow + 1)
        return false;

    int destinationRow = destinationChild;

    // move X above X+N+1 = move X to X+N
    if (destinationRow > (sourceRow + 1 ))
    {
        destinationRow--;
    }

    beginMoveRows(parent, sourceRow, sourceRow,
                  parent, destinationChild);
    QList<LeGraphicsItemLayer*> layers = d->m_scene->featureLayers();
    layers.move(sourceRow, destinationRow);
    d->m_scene->changeLayerOrdering(layers);
    endMoveRows();

    return true;
}
